#include "pch.h"
#include "SceneGraphRender.h"
#include "BasicShapes.h"
#include "BasicLoader.h"
#include "Exceptions.h"

#include "ModelSceneNode.h"

#include "WaveFrontObjLoader.h"

using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;

SceneGraphRender::SceneGraphRender()
{
	this->shapes = new ShapeLibrary();
	this->textures = new TextureLibrary();
	this->shaderPairs = new ShaderPairLibrary();

	this->models = new ModelLibrary();

	this->sceneGraph = new SceneNode();

	this->listOfRenderables = new List();
}

void SceneGraphRender::CreateDeviceIndependentResources()
{
    AbstractRender::CreateDeviceIndependentResources();
}

void SceneGraphRender::CreateDeviceResources()
{
    AbstractRender::CreateDeviceResources();

    BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());

	// initialize the shadera
	ShaderPair* shaderPair = 
		new ShaderPair(
			m_d3dDevice.Get(), 
			L"SimpleVertexShader.cso", 
			L"SimplePixelShader.cso");

	int shaderID = this->shaderPairs->Add(shaderPair);

   
	///////////////////////////////////////////////////////////////////////////
    // initialize the 3d shapes
    BasicShapes^ shapeBuilder = ref new BasicShapes(m_d3dDevice.Get());

	float heights[] = {
		0.0f, 0.0f, 0.0f,
		0.0f, 0.5f, 0.0f,
		0.0f, 0.0f, 0.0f};

	Shape* cube = shapeBuilder->CreateBoxModel(0.1f,0.1f,0.1f,0.1f,0.1f,0.1f);
	int shapeID = this->shapes->Add(cube);

	////////////////////////////////////////////////////////////////////////////
	// initializ the textures
	Texture* texture = 
		new Texture(
			m_d3dDevice.Get(),
			L"TextureCube.png");

	int textureID = this->textures->Add(texture);

	Texture* redTexture = 
		new Texture(
			m_d3dDevice.Get(),
			L"Barrier.png");

	int redTextureID = 
		this->textures->Add(redTexture);

	///////////////////////////////////////////////////////////////////////////
	// Create the simple model
	Model* cubeModel = new Model();
	cubeModel->ShapeID = shapeID;
	cubeModel->ShaderID = shaderID;
	cubeModel->TextureID = textureID;

	// add the mpdel to the library
	int modelID = models->Add(cubeModel);

	ModelSceneNode* floor = new ModelSceneNode();
	floor->ModelID = modelID;

	WaveFrontObjLoader* waveFrontObjLoader = new WaveFrontObjLoader(m_d3dDevice.Get());

	int numberOfShapes;
	Shape** shapesLoaded;

	waveFrontObjLoader->LoadWaveFrontShapes(L"barrier.obj", &shapesLoaded, &numberOfShapes);

	if (shapesLoaded != NULL)
	{
		SceneNode* watchTowerSceneNode = new SceneNode();

		for(int i = 0; i < numberOfShapes; i ++)
		{
			shapeID = this->shapes->Add(shapesLoaded[i]);

			Model* obj = new Model();
			obj->ShapeID = shapeID;
			obj->ShaderID = shaderID;
			obj->TextureID = redTextureID;

			int objID = models->Add(obj);

			ModelSceneNode* objSceneNode = new ModelSceneNode();
			objSceneNode->ModelID = objID;

			watchTowerSceneNode->Add(objSceneNode);

		}

		this->sceneGraph->Add(watchTowerSceneNode);

		//free(shapesLoaded);
	}

	


	///////////////////////////////////////////////////////////////////////////
	// Initialise the scene graph
	/*ModelSceneNode *node = new ModelSceneNode();
	node->ModelID = modelID;

	this->sceneGraph->Add(node);

	SceneNode* bone = new SceneNode();
	bone->localTrandormation = translation(0.0f, 1.0f, 0.0f);

	node->Add(bone);

	ModelSceneNode *node1 = new ModelSceneNode();
	node1->ModelID = modelID;

	bone->Add(node1);*/

	////////////////////////////////////////////////////////////////////////////
	// Create and initialize a Mecha
	mecha = new BipedalMecha();
	mecha->SetBasicMechaDimensions(
		0.125f, 0.25f, 0.25, 0.25f, 0.25f, 
		0.25f, 0.125f, 0.3f, 0.3f, 0.1f, 0.05f,
		0.15f, 0.45f, 0.45f, 0.05f, 0.1f, 0.05f,
		true,
		m_d3dDevice.Get(),
		shapes,
		textureID,
		shaderID,
		models);

	mecha->RotateLeftKnee(45.0f);

	//this->sceneGraph->Add(floor);
	// this->sceneGraph->Add(mecha);
	
	///////////////////////////////////////////////////////////////////////////
    // create the constant buffer for updating model and camera data
    CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
            &constantBufferDesc,
            nullptr,             // leave the buffer uninitialized
            &m_constantBuffer
            )
        );

    // create the sampler
    D3D11_SAMPLER_DESC samplerDesc;
    ZeroMemory(&samplerDesc, sizeof(D3D11_SAMPLER_DESC));
    samplerDesc.Filter = D3D11_FILTER_ANISOTROPIC;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.MipLODBias = 0.0f;
    // use 4x on feature level 9.2 and above, otherwise use only 2x
    samplerDesc.MaxAnisotropy = m_featureLevel > D3D_FEATURE_LEVEL_9_1 ? 4 : 2;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
    samplerDesc.BorderColor[0] = 0.0f;
    samplerDesc.BorderColor[1] = 0.0f;
    samplerDesc.BorderColor[2] = 0.0f;
    samplerDesc.BorderColor[3] = 0.0f;
    // allow use of all mip levels
    samplerDesc.MinLOD = 0;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

    DX::ThrowIfFailed(
        m_d3dDevice->CreateSamplerState(
            &samplerDesc,
            &m_sampler)
        );

    m_camera = new BasicCamera();
}

void SceneGraphRender::CreateWindowSizeDependentResources()
{
    AbstractRender::CreateWindowSizeDependentResources();

    // update the 3D projection matrix
    m_camera->SetProjectionParameters(
        70.0f,                                                // use a 70-degree vertical field of view
        m_renderTargetSize.Width / m_renderTargetSize.Height, // specify the aspect ratio of the window
        0.1f,                                                // specify the nearest Z-distance at which to draw vertices
        100.0f                                                // specify the farthest Z-distance at which to draw vertices
        );

    // save the new projection matrix to the constant buffer data struct
    m_camera->GetProjectionMatrix(&m_modelViewProjection.projection);
}

void SceneGraphRender::Update(float timeTotal, float timeDelta)
{
    // update the model matrix based on the time
	// this->sceneGraph->worldTransformation = rotationY(timeTotal * 30.0f);
	float4x4 camera_pos =
		mul(rotationY(timeTotal * 30.0f), translation(0, 8.0f, 2.0f));

	float3 pos = float3(camera_pos._14, camera_pos._24, camera_pos._34);

    // update the view matrix based on the camera position
    // note that for this sample, the camera position is fixed
    m_camera->SetViewParameters(
        pos,
        float3(0, 0.0f, 0),
        //float3(0, 1, 0)
		m_camera->GetUp( pos, float3(0, 0.0f, 0) )
        );

    m_camera->GetViewMatrix(&m_modelViewProjection.view);

	this->listOfRenderables->Clear();
	this->sceneGraph->ResolveModelTransformation(this->m_camera, this->listOfRenderables);

}

void SceneGraphRender::Render()
{
    m_d3dContext->OMSetRenderTargets(
        1,
        m_d3dRenderTargetView.GetAddressOf(),
        m_d3dDepthStencilView.Get()
        );

    // clear both the render target and depth stencil to default values
    const float ClearColor[4] = { 0.071f, 0.040f, 0.561f, 1.0f };
    m_d3dContext->ClearRenderTargetView(
        m_d3dRenderTargetView.Get(),
        ClearColor
        );

    m_d3dContext->ClearDepthStencilView(
        m_d3dDepthStencilView.Get(),
        D3D11_CLEAR_DEPTH,
        1.0f,
        0
        );

	// Render the scenegraph
	SceneNode::RendSceneNodes(
		m_d3dContext.Get(),
		m_modelViewProjection,
		m_constantBuffer,
		m_sampler,

		shapes,
		textures,
		shaderPairs,
		models,

		listOfRenderables);
}

void SceneGraphRender::CleanUpResources()
{

}